package com.h2cloud.depends;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.params.SetParams;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author: wulongkai
 * @create: 2022-10-10 16:24
 **/
@Slf4j
@Component
@Data
@Order(value = 1)
public class RedisClient implements ApplicationRunner {

    private static final Long SUCCESS = 1L;

    private static final String LOCK_SUCCESS = "OK";
    private static final Long RELEASE_SUCCESS = 1L;

    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    private JedisPool jedisPool;

    @Value("${redis.maxTotal}")
    private int maxTotal;

    @Value("${redis.minIdle}")
    private int minIdle;

    @Value("${redis.isBlockWhenExhausted}")
    private boolean isBlockWhenExhausted;

    @Value("${redis.maxWaitMillis}")
    private long maxWaitMillis;

    @Value("${redis.minEvictableIdleTimeMillis}")
    private long minEvictableIdleTimeMillis;

    @Value("${redis.numTestsPerEvictionRun}")
    private int numTestsPerEvictionRun;

    @Value("${redis.isTestWhileIdle}")
    private boolean isTestWhileIdle;

    @Value("${redis.timeBetweenEvictionRunsMillis}")
    private long timeBetweenEvictionRunsMillis;

    @Value("${redis.host}")
    private String host;

//    @Value("${redis.password}")
//    private String password;

    @Value("${redis.port}")
    private int port;

    @Value("${redis.timeout}")
    private int timeout;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(maxTotal);
        config.setMinIdle(minIdle);
        config.setBlockWhenExhausted(isBlockWhenExhausted);
        config.setMaxWaitMillis(maxWaitMillis);
        config.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        config.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
        config.setTestWhileIdle(isTestWhileIdle);
        config.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        //测试环境
//        jedisPool = new JedisPool(config, host, port, timeout, password);
        jedisPool = new JedisPool(config, host, port, timeout);
    }

    public String get(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.get(key);
        } catch (Exception e) {
            log.error("EHR redis get key:{}, err:{}", key, e);
            throw e;
        }
    }

    public byte[] get(byte[] key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.get(key);
        } catch (Exception e) {
            log.error("EHR redis get key:{}, err:{}", key, e);
            throw e;
        }
    }

    public String hmset(String key, Map<String, String> hash) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.hmset(key, hash);
        } catch (Exception e) {
            log.error("EHR redis hmset key:{}, err:{}", key, e);
            throw e;
        }
    }

    public String scriptLoad(String script) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.scriptLoad(script);
        } catch (Exception e) {
            log.error("EHR redis scriptLoad script:{}, err:{}", script, e);
            throw e;
        }
    }

    public String evalsha(String sha, List<String> keys, List<String> args) {
        try (Jedis jedis = jedisPool.getResource()) {
            return String.valueOf(jedis.evalsha(sha, keys, args));
        } catch (Exception e) {
            log.error("EHR redis evalsha sha:{},keys:{}, err:{}", sha, e, keys);
            throw e;
        }
    }

    public String set(String key, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.set(key, value);
        } catch (Exception e) {
            log.error("EHR redis set key:{}, value:{}, err:{}", key, value, e);
            throw e;
        }
    }

    public String set(byte[] key, byte[] value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.set(key, value);
        } catch (Exception e) {
            log.error("EHR redis set key:{}, value:{}, err:{}", key, value, e);
            throw e;
        }
    }

    public Long setnx(String key, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.setnx(key, value);
        } catch (Exception e) {
            log.error("EHR redis setnx key:{}, value:{}err:{}", key, value, e);
            throw e;
        }
    }

    public String setex(String key, int seconds, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.setex(key, seconds, value);
        } catch (Exception e) {
            log.error("EHR redis setex key:{}, value:{}, secondes:{}, err:{}", key, value, seconds, e);
            throw e;
        }
    }

    public String setex(byte[] key, int seconds, byte[] value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.setex(key, seconds, value);
        } catch (Exception e) {
            log.error("EHR redis setex key:{}, value:{}, seconds:{}, err:{}", key, value, seconds, e);
            throw e;
        }
    }

    public Long incr(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.incr(key);
        } catch (Exception e) {
            log.error("EHR redis setnx key:{}err:{}", key, e);
            throw e;
        }
    }

    public String setNxEx(String key, int seconds, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.set(key, value, SetParams.setParams().ex(seconds).nx());
        } catch (Exception e) {
            log.error("EHR redis setNxEx key:{}, value:{}, secondes:{}, err:{}", key, value, seconds, e);
            throw e;
        }
    }


    public Long del(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.del(key);
        } catch (Exception e) {
            log.error("EHR redis del key:{}, err:{}", key, e);
            throw e;
        }
    }

    public Long expire(String key, int seconds) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.expire(key, seconds);
        } catch (Exception e) {
            log.error("EHR redis expire key:{},seconds:{}, err:{}", key, seconds, e);
            throw e;
        }
    }

    /**
     * 释放分布式锁
     *
     * @param lockKey   锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public boolean releaseDistributedLock(String lockKey, String requestId) {
        try (Jedis jedis = jedisPool.getResource()) {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
            return RELEASE_SUCCESS.equals(result);
        } catch (Exception e) {
            log.error("执行releaseDistributedLock异常: " + lockKey, e);
            return false;
        }
    }

    /**
     * 尝试获取分布式锁
     *
     * @param lockKey    锁
     * @param requestId  请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
        String result = setNxEx(lockKey, expireTime, requestId);
        return LOCK_SUCCESS.equals(result);
    }

    public boolean exist(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.exists(key);
        } catch (Exception e) {
            log.error("查询存在失败, key: {}", key);
            return false;
        }
    }


    /**
     * 指定key 失效剩余时间
     */
    public Long ttl(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.ttl(key);
        } catch (Exception e) {
            log.error("EHR redis del key:{}, err:{}", key, e);
            return null;
        }
    }


    /**
     * 操作set集合--add
     *
     * @param key 集合名称
     * @param val 要添加的元素
     * @return 成功的个数
     */
    public long sAdd(String key, String... val) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.sadd(key, val);
        } catch (Exception e) {
            log.error("redis设置set集合失败, key:{}, val:{}", key, val);
            throw e;
        }
    }

    /**
     * 移除set集合中的指定元素
     *
     * @param key 集合名称
     * @param val 要移除的元素
     * @return 移除成功的个数
     */
    public long sRem(String key, String... val) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.srem(key, val);
        } catch (Exception e) {
            log.error("redis移除set集合指定元素失败, key:{}, val:{}", key, val);
            throw e;
        }
    }

    /**
     * 获取set集合中的元素
     *
     * @param key 集合名称
     * @return 集合中的元素
     */
    public Set<String> sMembers(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.smembers(key);
        } catch (Exception e) {
            log.error("redis获取指定集合下的元素失败, key:{}", key);
            throw e;
        }
    }

    public Jedis getJedis() {
        Jedis jedis = jedisPool.getResource();
        return jedis;
    }

    /*########################  hash(哈希表)的操作  #######################*/
    //hset hget hmset hmget hgetall hdel hkeys hvals hexists hincrby

    /**
     * 给某个hash表设置一个键值对
     *
     * @param key
     * @param field
     * @param value
     */
    public void hset(String key, String field, String value) {

        try (Jedis jedis = jedisPool.getResource()) {
            jedis.hset(key, field, value);
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 取出某个hash表中某个field对应的value
     *
     * @param key   key
     * @param field field
     * @return
     */
    public String hget(String key, String field) {
        String hget = null;
        try (Jedis jedis = jedisPool.getResource()) {
            hget = jedis.hget(key, field);
        } catch (Exception e) {
            throw e;
        }
        return hget;
    }


    /**
     * 判断某个hash表中的某个key是否存在
     *
     * @param key
     * @param field
     * @return
     */
    public Boolean hexists(String key, String field) {
        Boolean exists = null;
        try (Jedis jedis = jedisPool.getResource()) {
            exists = jedis.hexists(key, field);
        } catch (Exception e) {
            throw e;
        }
        return exists;
    }

    public void hdel(String key, String... fields) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.hdel(key, fields);
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 返回某个hash表中所有的key
     *
     * @param key
     * @return
     */
    public Set<String> hkeys(String key) {
        Set<String> keys = null;
        try (Jedis jedis = jedisPool.getResource()) {
            keys = jedis.hkeys(key);
        } catch (Exception e) {
            throw e;
        }
        return keys;
    }

}


